#include <string.h>
#include <stdio.h>
#include "net.h"
#include "arp.h"
#include "ethernet.h"
#include "linklist.h"

/**
 * @brief 初始的arp包
 * 
 */
static const arp_pkt_t arp_init_pkt = {
    .hw_type16 = constswap16(ARP_HW_ETHER),
    .pro_type16 = constswap16(NET_PROTOCOL_IP),
    .hw_len = NET_MAC_LEN,
    .pro_len = NET_IP_LEN,
    .sender_ip = NET_IF_IP,
    .sender_mac = NET_IF_MAC,
    .target_mac = {0}};

/**
 * @brief arp地址转换表，<ip,mac>的容器
 * 
 */
map_t arp_table;

/**
 * @brief arp buffer，<ip,buf_t>的容器
 * 
 */
map_t arp_buf;
/**
 * @brief arp buffer list，<ip,node>的容器
 * 
 */
map_t arp_buf_list;

/**
 * @brief 打印一条arp表项
 * 
 * @param ip 表项的ip地址
 * @param mac 表项的mac地址
 * @param timestamp 表项的更新时间
 */
void arp_entry_print(void *ip, void *mac, time_t *timestamp)
{
    printf("%s | %s | %s\n", iptos(ip), mactos(mac), timetos(*timestamp));
}

/**
 * @brief 打印整个arp表
 * 
 */
void arp_print()
{
    printf("===ARP TABLE BEGIN===\n");
    map_foreach(&arp_table, arp_entry_print);
    printf("===ARP TABLE  END ===\n");
}
/**
 * @brief 发送一个arp请求
 * 
 * @param target_ip 想要知道的目标的ip地址
 */
void arp_req(uint8_t *target_ip)
{
    buf_init(&txbuf, sizeof(arp_pkt_t)); 
    arp_pkt_t pkt = arp_init_pkt;  // 相当于复制一份给pkt,不过pkt不能是指针
    pkt.opcode16 = constswap16(ARP_REQUEST);
    memcpy(pkt.target_ip, target_ip, NET_IP_LEN);
    memcpy(txbuf.data, &pkt, sizeof(arp_pkt_t));
    ethernet_out(&txbuf, ether_broadcast_mac, NET_PROTOCOL_ARP); // 以ARP协议发送出去
}

/**
 * @brief 发送一个arp响应
 * 
 * @param target_ip 目标ip地址
 * @param target_mac 目标mac地址
 */
void arp_resp(uint8_t *target_ip, uint8_t *target_mac)
{
    buf_init(&txbuf, sizeof(arp_pkt_t));
    arp_pkt_t pkt = arp_init_pkt;
    pkt.opcode16 = constswap16(ARP_REPLY);
    memcpy(pkt.target_ip, target_ip, NET_IP_LEN);
    memcpy(pkt.target_mac, target_mac, NET_MAC_LEN);
    memcpy(txbuf.data, &pkt, sizeof(arp_pkt_t));
    ethernet_out(&txbuf, target_mac, NET_PROTOCOL_ARP); 
}

/**
 * @brief arp报文报头检查
 *        报文完整,返回1;否则返回0
*/
int arp_check_hdr(arp_pkt_t *pkt){
    uint16_t pktHwType16 = constswap16(pkt->hw_type16);
    int i1 = (pktHwType16 == ARP_HW_ETHER);
    uint16_t pktProType16 = constswap16(pkt->pro_type16); 
    int i2 = (pktProType16 == NET_PROTOCOL_IP);
    int i3 = (pkt->hw_len == NET_MAC_LEN);
    int i4 = (pkt->pro_len == NET_IP_LEN);
    uint16_t pktOpcode16 = constswap16(pkt->opcode16);
    int i5 = (pktOpcode16 ==  ARP_REQUEST || pktOpcode16 == ARP_REPLY);
    if(i1 && i2 && i3 && i4 && i5){
        return 1;
    }else{
        return 0;
    }
}

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void arp_in(buf_t *buf, uint8_t *src_mac)
{
    // 报文长度检查
    if(buf->len < sizeof(arp_pkt_t)){
        printf("\narp_in: len(buf) < size(arp_pkt_t),discard\n");
        return;
    }
    // 报头检查
    arp_pkt_t *pkt = (arp_pkt_t *)(buf->data);
    if(arp_check_hdr(pkt) == 0){
        printf("\narp_in: arp_check_hdr fail\n");
        return;
    }
    // 更新 ARP表 的表项
    map_set(&arp_table, pkt->sender_ip, pkt->sender_mac);
    printf("\narp_in: update arp Table %s -> %s\n", iptos(pkt->sender_ip), mactos(pkt->sender_mac));
    // 查看该报文是否有相应的arp_buf缓存

    // buf_t *arpBuf = map_get(&arp_buf, pkt->sender_ip);
    // if(arpBuf != NULL){
    //     // 发送该数据
    //     printf("\n发送IP(%s)对应的缓存数据包\n", iptos(pkt->sender_ip));
    //     ethernet_out(arpBuf, pkt->sender_mac, NET_PROTOCOL_IP);
    //     map_delete(&arp_buf, pkt->sender_ip);
    // }

    buf_t *arpBuf = map_get(&arp_buf, pkt->sender_ip);
    // 若有待发送的数据包
    if (arpBuf != NULL){   
        printf("\narp_in: %s has buf to be sent\n", iptos(pkt->sender_ip));
        node *pH = map_get(&arp_buf_list, pkt->sender_ip);
        node *p = pH;
        while (NULL != p)
        {
            printf("\narp_in: get buf from cache, address: %p\n", p);
            ethernet_out(&(p->buf), pkt->sender_mac, NET_PROTOCOL_IP);
            p = p->next;
        }
        // 删除链表 从arp_buf中删除
        map_delete(&arp_buf, pkt->sender_ip);
        map_delete(&arp_buf_list, pkt->sender_ip);
        //clear_list(pH); // 不知道为何释放不了内存
        return;
    }

    // 若没有待发送的数据包
    uint16_t t = constswap16(pkt->opcode16);
    int i1 = ARP_REQUEST == t;
    int i2 = memcmp(net_if_ip, pkt->target_ip, NET_IP_LEN) == 0;
    if(i1 && i2){
        // 认为是请求本机的MAC地址的ARP请求报文
        printf("\narp_in: send ARP response msg to IP(%s)\n", iptos(pkt->sender_ip));
        arp_resp(pkt->sender_ip, pkt->sender_mac);  // 单播
        return;
    }
}

/**
 * @brief 处理一个要发送的数据包
 * 
 * @param buf 要处理的数据包
 * @param ip 目标ip地址
 * @param protocol 上层协议 不一定是ARP协议,还有可能是IP协议
 */
void arp_out(buf_t *buf, uint8_t *ip)
{
    uint8_t* resMAC = map_get(&arp_table, ip);
    // ARP表有缓存
    if(resMAC != NULL){
        printf("\narp_out: ARP table has mac for IP(%s): %s\n", iptos(ip), mactos(resMAC));
        ethernet_out(buf, resMAC, NET_PROTOCOL_IP); 
        return;
    }
    // ARP表没有有缓存
    printf("\narp_out: ARP table has no mac for IP(%s)\n", iptos(ip));

    // buf_t *arpBuf = map_get(&arp_buf, ip);
    // if(arpBuf == NULL){
    //     printf("\narp_buf中没有IP(%s)对应的数据包缓存\n", iptos(ip));
    //     map_set(&arp_buf, ip, buf);
    //     arp_req(ip);
    //     return;
    // }

    buf_t *arpBuf = map_get(&arp_buf, ip);
    // arp_buf中没有相应包
    if(arpBuf == NULL){
        printf("\narp_out: ARP cache has no packet for IP(%s)\n", iptos(ip));
        map_set(&arp_buf, ip, buf);
        node *pH = create_node(buf);
        map_set(&arp_buf_list, ip, pH);
        arp_req(ip);
        return;
    }
    // arp_buf中有相应包
    printf("\narp_out: ARP cache has packet for IP(%s)\n", iptos(ip));
    node *p = create_node(buf);
    node *pH = map_get(&arp_buf_list, ip);
    tail_insert(pH, p);
    return;
}

/**
 * @brief 初始化arp协议
 * 
 */
void arp_init()
{
    // uint64_t interval = 0x7fffffffffffffff;
    map_init(&arp_table, NET_IP_LEN, NET_MAC_LEN, 0, ARP_TIMEOUT_SEC, NULL);
    // map_init(&arp_buf, NET_IP_LEN, sizeof(buf_t), 0, ARP_MIN_INTERVAL, buf_copy);
    // map_init(&arp_buf_list, NET_IP_LEN, sizeof(node),0, ARP_MIN_INTERVAL, node_copy); 
    map_init(&arp_buf, NET_IP_LEN, sizeof(buf_t), 0, 0, buf_copy);
    map_init(&arp_buf_list, NET_IP_LEN, sizeof(node),0, 0, node_copy); 
    net_add_protocol(NET_PROTOCOL_ARP, arp_in);
    arp_req(net_if_ip);
}